package com.bstek.urule.console.servlet.diagram;

import com.bstek.urule.Utils;
import com.bstek.urule.builder.KnowledgeBase;
import com.bstek.urule.builder.KnowledgeBuilder;
import com.bstek.urule.builder.ResourceBase;
import com.bstek.urule.console.servlet.base.BaseController;
import com.bstek.urule.console.servlet.respackage.HttpSessionKnowledgeCache;
import com.bstek.urule.console.servlet.respackage.PackageController;
import com.bstek.urule.model.Node;
import com.bstek.urule.model.rete.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 *
 * </p>
 *
 * @author zhouchaoliang
 * @since 2021/12/9
 */
@Controller
public class ReteDiagramController extends BaseController {
    @Autowired
    private KnowledgeBuilder knowledgeBuilder;
    @Autowired
    private HttpSessionKnowledgeCache httpSessionKnowledgeCache;

    private ReteNodeLayout nodeLayout;

    private final int nodeWidth = 30;
    private final int nodeHeight = 30;

    public ReteDiagramController() {
        nodeLayout = new ReteNodeLayout();
    }

    @GetMapping("/urule/retediagram")
    public ModelAndView diagram(HttpServletRequest req, HttpServletResponse resp) {
        ModelAndView mv = new ModelAndView("/rete-diagram");
        mv.addObject("contextPath", req.getContextPath());
        mv.addObject("files", req.getParameter("files"));
        return mv;
    }

    @ResponseBody
    @PostMapping("/urule/retediagram/loadReteDiagramData")
    public Diagram loadReteDiagramData(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String files=req.getParameter("files");
        files= Utils.decodeURL(files);
        KnowledgeBase knowledgeBase=(KnowledgeBase)httpSessionKnowledgeCache.get(req, PackageController.KB_KEY);
        if(knowledgeBase==null){
            ResourceBase resourceBase=knowledgeBuilder.newResourceBase();
            String[] paths=files.split(";");
            for(String path:paths){
                String[] subpaths=path.split(",");
                path=subpaths[0];
                String version=subpaths[1];
                path=Utils.toUTF8(path);
                resourceBase.addResource(path,version);
            }
            knowledgeBase=knowledgeBuilder.buildKnowledgeBase(resourceBase);
            httpSessionKnowledgeCache.put(req,PackageController.KB_KEY, knowledgeBase);
        }
        Rete rete=knowledgeBase.getRete();
        Diagram diagram=buildReteDiagram(rete);
        return diagram;
    }

    private Diagram buildReteDiagram(Rete rete){
        Map<Node,NodeInfo> nodeMap=new HashMap<Node,NodeInfo>();
        List<Edge> edges=new ArrayList<Edge>();
        NodeInfo root=new NodeInfo();
        DiagramContext context=new DiagramContext(edges,nodeMap);
        root.setId(context.nextId());
        root.setLabel("Enter");
        root.setColor("#98AFC7");
        root.setWidth(nodeWidth);
        root.setHeight(nodeHeight);
        root.setRoundCorner(10);
        List<ObjectTypeNode> typeNodes=rete.getObjectTypeNodes();
        int level=1;
        for(ObjectTypeNode typeNode:typeNodes){
            NodeInfo node=new NodeInfo();
            node.setId(context.nextId());
            node.setLabel("T");
            node.setTitle(typeNode.getObjectTypeClass());
            node.setColor("#97CBFF");
            node.setLevel(level);
            node.setWidth(nodeWidth);
            node.setHeight(nodeHeight);
            node.setRoundCorner(5);
            root.addChild(node);
            List<Line> lines=typeNode.getLines();
            if(lines==null){
                continue;
            }
            int nextLevel=level+1;
            for(Line line:lines){
                Edge edge=new Edge(root.getId(),node.getId());
                edges.add(edge);
                buildLine(line,context,node,nextLevel);
            }
        }
        Box box=nodeLayout.layout(root);
        Diagram diagram = new Diagram(edges,root);
        if(box!=null){
            diagram.setWidth(box.getWidth()+500);
            diagram.setHeight(box.getHeight()+300);
        }
        return diagram;
    }

    private void buildLine(Line line,DiagramContext context,NodeInfo parentNode,int level){
        Node toNode=line.getTo();
        if(toNode==null){
            return;
        }
        Map<Node,NodeInfo> nodeMap=context.getNodeMap();
        NodeInfo newNodeInfo=null;
        if(nodeMap.containsKey(toNode)){
            newNodeInfo=nodeMap.get(toNode);
            context.addEdge(new Edge(parentNode.getId(),newNodeInfo.getId()));
            return;
        }
        List<Line> lines=null;
        newNodeInfo=new NodeInfo();
        newNodeInfo.setLevel(level);
        newNodeInfo.setId(context.nextId());
        newNodeInfo.setWidth(nodeWidth);
        newNodeInfo.setHeight(nodeHeight);
        if(toNode instanceof CriteriaNode){
            CriteriaNode cnode=(CriteriaNode)toNode;
            newNodeInfo.setColor("#B3D9D9");
            newNodeInfo.setLabel("C");
            newNodeInfo.setTitle(cnode.getCriteriaInfo());
            newNodeInfo.setRoundCorner(nodeHeight);
            lines=cnode.getLines();
        }else if(toNode instanceof NamedCriteriaNode){
            NamedCriteriaNode node=(NamedCriteriaNode)toNode;
            newNodeInfo.setColor("#B3E9D9");
            newNodeInfo.setLabel("C");
            newNodeInfo.setTitle(node.getCriteriaInfo());
            newNodeInfo.setRoundCorner(nodeHeight);
            lines=node.getLines();
        }else if(toNode instanceof AndNode){
            AndNode andNode=(AndNode)toNode;
            lines=andNode.getLines();
            newNodeInfo.setColor("#DAB1D5");
            newNodeInfo.setLabel("AND");
            newNodeInfo.setRoundCorner(nodeHeight/2);
        }else if(toNode instanceof OrNode){
            OrNode orNode=(OrNode)toNode;
            lines=orNode.getLines();
            newNodeInfo.setColor("#82D900");
            newNodeInfo.setLabel("OR");
            newNodeInfo.setRoundCorner(nodeHeight/2);
        }else if(toNode instanceof TerminalNode){
            TerminalNode terminalNode=(TerminalNode)toNode;
            newNodeInfo.setColor("orange");
            newNodeInfo.setLabel(terminalNode.getRule().getName());
            newNodeInfo.setTitle(terminalNode.getRule().getName());
            newNodeInfo.setRoundCorner(0);
        }
        nodeMap.put(toNode, newNodeInfo);
        parentNode.addChild(newNodeInfo);
        context.addEdge(new Edge(parentNode.getId(),newNodeInfo.getId()));
        if(lines==null){
            return;
        }
        int nextLevel=level+1;
        for(Line nextLine:lines){
            buildLine(nextLine, context, newNodeInfo,nextLevel);
        }
    }
}
